//! Utility functions for the chess engine
//!
//! This module provides helper functions for coordinate conversion,
//! game state serialization, and move notation.

use std::fmt;
use serde::{Serialize, Deserialize};
use crate::{Position, Side, ChessResult, ChessError};
use crate::piece::PieceType;
use crate::moves::Move;
use crate::game::{Game, MoveRecord};

/// Chinese chess coordinate notation (一二三...)
const CHINESE_NUMBERS: [char; 9] = ['一', '二', '三', '四', '五', '六', '七', '八', '九'];

/// Traditional Chinese piece names
#[derive(Debug, Clone, Copy)]
pub struct ChinesePieceNames {
    pub red: char,
    pub black: char,
}

impl PieceType {
    /// Get the Chinese names for the piece type
    fn chinese_names(&self) -> ChinesePieceNames {
        match self {
            PieceType::General => ChinesePieceNames { red: '帥', black: '將' },
            PieceType::Advisor => ChinesePieceNames { red: '仕', black: '士' },
            PieceType::Elephant => ChinesePieceNames { red: '相', black: '象' },
            PieceType::Horse => ChinesePieceNames { red: '傌', black: '馬' },
            PieceType::Chariot => ChinesePieceNames { red: '俥', black: '車' },
            PieceType::Cannon => ChinesePieceNames { red: '炮', black: '砲' },
            PieceType::Soldier => ChinesePieceNames { red: '兵', black: '卒' },
        }
    }
}

/// Represents a move in Chinese notation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChineseMove {
    pub piece_name: char,
    pub from_file: char,
    pub action: char,  // 进(forward) or 退(backward)
    pub to_file: char,
}

impl fmt::Display for ChineseMove {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}{}{}{}", self.piece_name, self.from_file, self.action, self.to_file)
    }
}

/// Convert position to Chinese coordinates
pub fn position_to_chinese(pos: Position, side: Side) -> (char, char) {
    let file = match side {
        Side::Red => CHINESE_NUMBERS[8 - pos.file as usize],
        Side::Black => CHINESE_NUMBERS[pos.file as usize],
    };
    let rank = CHINESE_NUMBERS[pos.rank as usize];
    (file, rank)
}

/// Convert Chinese coordinates to position
pub fn chinese_to_position(file: char, rank: char, side: Side) -> ChessResult<Position> {
    let file_idx = CHINESE_NUMBERS.iter().position(|&x| x == file)
        .ok_or(ChessError::InvalidPosition)?;
    
    let rank_idx = CHINESE_NUMBERS.iter().position(|&x| x == rank)
        .ok_or(ChessError::InvalidPosition)?;
    
    let file = match side {
        Side::Red => 8 - file_idx,
        Side::Black => file_idx,
    };
    
    Position::new(file as i8, rank_idx as i8)
        .ok_or(ChessError::InvalidPosition)
}

/// Convert a move to Chinese notation
pub fn move_to_chinese(game: &Game, mv: &MoveRecord) -> ChineseMove {
    let piece = mv.piece_moved;
    let names = piece.piece_type.chinese_names();
    let piece_name = match piece.side {
        Side::Red => names.red,
        Side::Black => names.black,
    };
    
    let (from_file, _) = position_to_chinese(mv.mv.from, piece.side);
    let (to_file, _) = position_to_chinese(mv.mv.to, piece.side);
    
    let action = if (piece.side == Side::Red && mv.mv.to.rank > mv.mv.from.rank) ||
                   (piece.side == Side::Black && mv.mv.to.rank < mv.mv.from.rank) {
        '进'
    } else {
        '退'
    };
    
    ChineseMove {
        piece_name,
        from_file,
        action,
        to_file,
    }
}

/// Game state serialization format
#[derive(Serialize, Deserialize)]
pub struct GameState {
    pub fen: String,
    pub moves: Vec<MoveRecord>,
    pub current_side: Side,
}

impl Game {
    /// Save the game state to a string
    pub fn save_state(&self) -> String {
        let state = GameState {
            fen: self.to_fen(),
            moves: self.history.clone(),
            current_side: self.current_side,
        };
        
        serde_json::to_string(&state).unwrap_or_default()
    }
    
    /// Load the game state from a string
    pub fn load_state(state_str: &str) -> ChessResult<Game> {
        let state: GameState = serde_json::from_str(state_str)
            .map_err(|_| ChessError::InvalidMove)?;
        
        let mut game = Game::new(); // Start with a new game
        game.current_side = state.current_side;
        game.history = state.moves;
        
        // Replay moves to reconstruct the board state
        for record in &game.history {
            game.board.move_piece(record.mv.from, record.mv.to)?;
        }
        
        Ok(game)
    }
}

/// Generate a simple evaluation score for the current position
pub fn evaluate_position(game: &Game) -> i32 {
    let mut score = 0;
    
    // Material value
    for (pos, piece) in game.board.get_pieces(Side::Red) {
        score += piece.value();
        
        // Position bonus
        score += match piece.piece_type {
            PieceType::Soldier if pos.rank > 4 => 10, // Advanced soldier
            PieceType::Horse | PieceType::Cannon if pos.rank > 4 => 5, // Advanced pieces
            _ => 0,
        };
    }
    
    for (pos, piece) in game.board.get_pieces(Side::Black) {
        score -= piece.value();
        
        // Position bonus
        score -= match piece.piece_type {
            PieceType::Soldier if pos.rank < 5 => 10, // Advanced soldier
            PieceType::Horse | PieceType::Cannon if pos.rank < 5 => 5, // Advanced pieces
            _ => 0,
        };
    }
    
    // Check bonus
    if game.is_in_check(Side::Black) {
        score += 50;
    }
    if game.is_in_check(Side::Red) {
        score -= 50;
    }
    
    score
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_position_conversion() {
        let pos = Position { file: 0, rank: 0 };
        let (file, rank) = position_to_chinese(pos, Side::Red);
        assert_eq!(file, '九');
        
        let pos = Position { file: 8, rank: 0 };
        let (file, rank) = position_to_chinese(pos, Side::Red);
        assert_eq!(file, '一');
    }

    #[test]
    fn test_game_state_serialization() {
        let game = Game::new();
        let state_str = game.save_state();
        let loaded_game = Game::load_state(&state_str);
        assert!(loaded_game.is_ok());
    }

    #[test]
    fn test_evaluation() {
        let game = Game::new();
        let score = evaluate_position(&game);
        assert_eq!(score, 0); // Initial position should be equal
    }
}